package dbutil

import (
	"fmt"
	"github.com/pkg/errors"
	"gorm.io/driver/mysql"
	"gorm.io/gorm"
	"gorm.io/gorm/schema"
	"math/rand"
	"publiclib/config"
	"time"
)

// 初始一份集群seed
var rd *rand.Rand

func init() {
	rd = rand.New(rand.NewSource(time.Now().Unix()))
}

// mysql 集群组
type MysqlCluster struct {
	masterDB []*gorm.DB
	slaveDB  []*gorm.DB
}

func (m *MysqlCluster) String() string {
	return fmt.Sprintf("MysqlCluster m.size: [%d], s.size: [%d]", len(m.masterDB), len(m.slaveDB))
}

func (m *MysqlCluster) GetDB() *gorm.DB {
	if len(m.masterDB) > 0 {
		return m.GetMasterDB()
	}
	return m.GetSlaveDB()
}

func (m *MysqlCluster) GetMasterDB() *gorm.DB {
	if len(m.masterDB) > 0 {
		num := rd.Intn(len(m.masterDB))
		return m.masterDB[num]
	}
	return nil
}

// 持续会话模式， 共享会话, 临时禁用事务， 效率提升
func (m *MysqlCluster) SessionM() *gorm.DB {
	db := m.GetMasterDB()
	if db != nil {
		return db.Session(&gorm.Session{SkipDefaultTransaction: true})
	}
	return nil
}

func (m *MysqlCluster) GetSlaveDB() *gorm.DB {
	if len(m.slaveDB) == 0 {
		return m.GetMasterDB()
	}
	num := rd.Intn(len(m.slaveDB))
	return m.slaveDB[num]
}

// 持续会话模式， 共享会话, 临时禁用事务， 效率提升
func (m *MysqlCluster) SessionS() *gorm.DB {
	db := m.GetSlaveDB()
	if db != nil {
		return db.Session(&gorm.Session{SkipDefaultTransaction: true})
	}
	return nil
}

func openDB(dsn string, cfg *gorm.Config) (*gorm.DB, error) {
	dialector := mysql.Open(dsn)
	return gorm.Open(dialector, cfg)
}

// 连接集群的初始
func InitMysql(opts *config.Conf) (IDBCluster, error) {
	if opts == nil {
		panic("MysqlCluster opts is nil.")
	}
	if len(opts.Mysql) == 0 {
		panic("MysqlCluster opts.Mysql is nil.")
	}
	mc := &MysqlCluster{
		masterDB: make([]*gorm.DB, 0, 1),
		slaveDB:  make([]*gorm.DB, 0, 4),
	}
	for _, mysqlc := range opts.Mysql {
		dsnList := mysqlc.GetURI()
		for _, dn := range dsnList {
			db, err := openDB(dn, &gorm.Config{
				NamingStrategy: schema.NamingStrategy{
					SingularTable: true,
				},
			})
			if err != nil {
				return nil, errors.Wrap(err, "open database")
			}

			sqlDB, err := db.DB()
			if err != nil {
				return nil, errors.Wrap(err, "get underlying *sql.DB")
			}
			sqlDB.SetMaxOpenConns(mysqlc.MaxActive)
			sqlDB.SetMaxIdleConns(mysqlc.MaxIdle)
			sqlDB.SetConnMaxLifetime(3 * time.Minute)

			ndb := db.Set("gorm:table_options", "ENGINE=InnoDB").Session(&gorm.Session{})
			// 添加可用的集群连接
			if mysqlc.Ct == config.CTMaster {
				mc.masterDB = append(mc.masterDB, ndb)
			} else {
				mc.slaveDB = append(mc.slaveDB, ndb)
			}
		}
	}
	localCluster = mc
	return mc, nil
}
